The Tasks class and the async and await keywords were introduced in .NET 4.5, providing a simpler structure for creating multi-threaded applications compared to the thread-based or single-threaded apartment models of the past. Its primary benefit is the ability to code multi-threaded/asynchronous applications with a straightforward logical structure that resembles synchronous code. Further features include passing parameters directly into the asynchronous function, directly returning return values, and catching exceptions directly around the asynchronous call. For our components, this also obsoletes the need for the Start and Marshal methods, along with the UserState and Error event (among others specific to each component).
Task.Run() may be used to execute the code specified in its lambda statement on a separate thread. Used by itself, the call immediately returns (while the lambda statement executes on its new thread), and continues on to the next line of code. When used in conjunction with await (and marking the containing method or event with the 'async' keyword), the next line of code will only be called once Task.Run returns (and, using await allows the method to return a value). While the awaited method executes on its thread, execution of the UI thread is returned to the caller of the method marked with the async keyword.
The multithreaded implementation of our 4.x components is compatible with these Tasks, and there is little difference between the use of the two; calls to PowerTCP components within Tasks are equal to calling them within a thread created with Start(). Where Start() and the UserState event were used previously:
C# |
Copy Code |
---|---|
private void button1_Click(object sender, EventArgs e) { myComponent.Start(myBlockingFunction, null); } private void myBlockingFunction(object myObject) { //This executes on a worker thread, so it could contain //any long-running code, and still not block the UI myComponent.Marshal("Message from the worker thread", null); } void myComponent_UserState(object sender, UserStateEventArgs e) { MessageBox.Show(e.Message); } |
Visual Basic |
Copy Code |
---|---|
Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles button1.Click myComponent.Start(AddressOf myBlockingFunction, Nothing) End Sub Private Sub myBlockingFunction(ByVal myObject As Object) 'This executes on a worker thread, so it could contain 'any long-running code, and still not block the UI myComponent.Marshal("Message from the worker thread", Nothing) End Sub Private Sub myComponent_UserState(ByVal sender As Object, ByVal e As UserStateEventArgs) MessageBox.Show(e.Message) End Sub |
You can now use:
C# |
Copy Code |
---|---|
private async void button1_Click(object sender, EventArgs e) { //This call will not block the UI thread string taskText = await myBlockingFunctionAsync(); MessageBox.Show(taskText); } private Task<string> myBlockingFunctionAsync() { //This Task.Run lambda statement could contain any long-running code, and still not block the UI return Task.Run<string>(() => { return "Message from Task"; }); } |
Visual Basic |
Copy Code |
---|---|
Private Async Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles button1.Click 'This call will not block the UI thread Dim taskText As String = Await myBlockingFunctionAsync() MessageBox.Show(taskText) End Sub Private Function myBlockingFunctionAsync() As Task(Of String) 'This Task.Run lambda statement could contain any long-running code, and still not block the UI Return Task.Run(Of String)(Function() "Message from Task") End Function |
Which is equivalent to:
C# |
Copy Code |
---|---|
private async void button1_Click(object sender, EventArgs e) { //This call will not block the UI thread string taskText = await Task.Run<string>(() => { return "Message from Task"; }); MessageBox.Show(taskText); } |
Visual Basic |
Copy Code |
---|---|
Private Async Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles button1.Click 'This call will not block the UI thread Dim taskText As String = Await Task.Run(Of String)(Function() "Message from Task") MessageBox.Show(taskText) End Sub |
While Task.Run() does generally obsolete the need for Start() and Marshal(), Marshal() may still be used to marshal data from the thread created with Task.Run() over to the UI thread to the UserState event or other events raised by Marshal(), as desired.